﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ClusterActioner.cs" company="DissingNelleman">
//   
// All rights reserved. </copyright>
// <summary>
//   The cluster actioner.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ServiceLibrary.Actioners.Activities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using NHibernate;

    using ServiceLibrary.Models.Base;
    using ServiceLibrary.Models.Domain;
    using ServiceLibrary.Services.Commands;
    using ServiceLibrary.Services.Queries;
    using ServiceLibrary.Services.Results;
    using NHibernate.Criterion;

    /// <summary>
    /// The cluster actioner.
    /// </summary>
    public class ClusterActioner : ActionerBase
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ClusterActioner"/> class.
        /// </summary>
        /// <param name="session">
        /// The session.
        /// </param>
        /// <param name="activeUser">
        /// The active user.
        /// </param>
        public ClusterActioner(ISession session, User activeUser)
            : base(session, activeUser)
        {
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The create cluster.
        /// </summary>
        /// <param name="cluster">
        /// The cluster.
        /// </param>
        /// <returns>
        /// The <see cref="Guid"/>.
        /// </returns>
        public Guid CreateCluster(Cluster cluster)
        {
            cluster.CreatedDateTime = DateTime.Now;
            this.Session.Save(cluster);

            this.Log.CreateLogEntry(
                cluster.ID, string.Format("Klyngen '{0}' blevet oprettet.", cluster.Name), Enums.LogEntryType.Cluster);

            return cluster.ID;
        }

        /// <summary>
        /// The create cluster.
        /// </summary>
        /// <param name="cmd">
        /// The clusterQuery.
        /// </param>
        public void CreateCluster(CreateClusterCommand cmd)
        {
            if (cmd.Cluster != null)
            {
                var cluster = new Cluster
                                  {
                                      ID = cmd.Cluster.ID,
                                      Name = cmd.Cluster.Name,
                                      Date = cmd.Cluster.Date,
                                      IsNational = cmd.Cluster.IsNational,
                                      Location = this.Session.Get<Location>(cmd.Cluster.Location)
                                  };

                if (cmd.Cluster.OwnerProjects != null && cmd.Cluster.OwnerProjects.Any())
                {
                    cluster.OwnerProjects = new HashSet<Project>();
                    foreach (var project in cmd.Cluster.OwnerProjects.Select(prID => this.Session.Get<Project>(prID)).Where(project => project != null))
                    {
                        cluster.OwnerProjects.Add(project);
                    }
                }

                if (cmd.Cluster.Profiles != null && cmd.Cluster.Profiles.Any())
                {
                    foreach (Guid profileID in cmd.Cluster.Profiles)
                    {
                        var profile = this.Session.Get<Profile>(profileID);
                        if (profile != null)
                        {
                            cluster.Profiles.Add(profile);
                        }
                    }
                }

                if (cmd.Cluster.Methods != null && cmd.Cluster.Methods.Any())
                {
                    foreach (Guid methodID in cmd.Cluster.Methods)
                    {
                        var method = this.Session.Get<ClusterMethod>(methodID);
                        if (method != null)
                        {
                            cluster.Methods.Add(method);
                        }
                    }
                }

                if (cmd.Cluster.Statuses != null && cmd.Cluster.Statuses.Any())
                {
                    foreach (Guid statusID in cmd.Cluster.Statuses)
                    {
                        var status = this.Session.Get<ClusterStatus>(statusID);
                        if (status != null)
                        {
                            cluster.Statuses.Add(status);
                        }
                    }
                }

                CreateCluster(cluster);
            }
        }

        /// <summary>
        /// The create cluster related objects.
        /// </summary>
        public void CreateClusterRelatedObjects()
        {
            IList<Topic> topics = this.Session.QueryOver<Topic>().List();
            var defaultTopics = new List<string>
                                    {
                                        "Forretningsplan", 
                                        "Business Case", 
                                        "Krav og Certificering", 
                                        "Understøttelse af produktudvikling", 
                                        "Salg og eksporthjælp", 
                                        "Finansiering", 
                                        "HR-udvikling - Relevante uddannelse/ kurser", 
                                        "HR-udvikling - Rekruttering af specialister", 
                                        "HR-udvikling - Træningskurser", 
                                        "HR-udvikling - Marketing", 
                                        "Iværksætter forløb - Vejledning/ coaching", 
                                        "Iværksætter forløb - Konkurrencer", 
                                        "Markedsinformation", 
                                        "Virksomhedsbesøg", 
                                        "Andet"
                                    };
            var topA = new TopicActioner(this.Session, this.ActiveUser);
            foreach (string topic in from topic in defaultTopics
                                     let alreadyExists = (from t in topics
                                                          where topic != null
                                                          && t.Title.Equals(topic)
                                                          select t).Any()
                                     where !alreadyExists
                                     select topic)
            {
                topA.CreateTopic(topic);
            }

            IList<ClusterMethodType> methodTypes = this.Session.QueryOver<ClusterMethodType>().List();

            var defaultMethodTypes = new List<string>
                                         {
                                             "Vidensindsamling", 
                                             "Tematiserede rapport", 
                                             "Metodebeskrivelse", 
                                             "Analyse", 
                                             "Strategi"
                                         };
            var metA = new ClusterMethodActioner(this.Session, this.ActiveUser);

            foreach (string methodType in from methodType in defaultMethodTypes let alreadyExists = methodTypes.Any(t => t.Name.Equals(methodType)) where !alreadyExists select methodType)
            {
                metA.CreateClusterMethodType(methodType);
            }

            IList<ClusterStatusType> status = this.Session.QueryOver<ClusterStatusType>().List();

            var statA = new ClusterStatusActioner(this.Session, this.ActiveUser);
            var defaultStatuses = new List<string>
                                      {
                                          "Kontakt men uden projekt", 
                                          "R&D samarbejde", 
                                          "Forretningssamarbejde (non R&D)", 
                                          "Planlagt at vi vil tage kontakt", 
                                          "Finansieret"
                                      };
            foreach (string methodType in from methodType in defaultStatuses let alreadyExists = (from t in status where t.Name.Equals(methodType) select t).Any() where !alreadyExists select methodType)
            {
                statA.CreateClusterStatusType(methodType);
            }
        }

        /// <summary>
        /// The execute search query.
        /// </summary>
        /// <param name="clusterQuery">
        /// The clusterQuery.
        /// </param>
        /// <returns>
        /// The <see cref="ClusterResult"/>.
        /// </returns>
        public ClusterResult ExecuteSearchQuery(ClusterQuery clusterQuery)
        {
            SearchSettings settings = clusterQuery.Settings;
            var result = new ClusterResult();

            IQueryOver<Cluster, Cluster> search = this.Session.QueryOver<Cluster>();
            this.ApplyDefaultCriterias(clusterQuery.Settings, search);
            this.ApplyDefaultSearchFieldCriteriasReportsSearch(clusterQuery.DefaultFields, search);

            if (clusterQuery.AtLocation.HasValue && clusterQuery.AtLocation.Value != Guid.Empty)
            {
                search = search.Where(x => x.Location.ID == clusterQuery.AtLocation.Value);
            }


            if (clusterQuery.MethodTypes != null && clusterQuery.MethodTypes.Any())
            {
                var methodTypesSearch = this.Session.QueryOver<ClusterMethodType>();
                methodTypesSearch.RootCriteria.Add(Restrictions.InG("ID", clusterQuery.MethodTypes));
                var methodTypes = methodTypesSearch.List();

                var methods = this.Session.QueryOver<ClusterMethod>()
                    .WhereRestrictionOn(x => x.Type).IsInG(methodTypes)
                    .List()
                    .Select(x => x.ID)
                    .ToList();

                search.RootCriteria.CreateAlias("Methods", "mts")
                          .Add(Restrictions.In("mts.ID", methods));
            }
            
            if (clusterQuery.StatusTypes != null && clusterQuery.StatusTypes.Any())
            {
                var statusTypesSearch = this.Session.QueryOver<ClusterStatusType>();
                statusTypesSearch.RootCriteria.Add(Restrictions.InG("ID", clusterQuery.StatusTypes));
                var statusTypes = statusTypesSearch.List();

                var statuses = this.Session.QueryOver<ClusterStatus>()
                    .WhereRestrictionOn(x => x.Type).IsInG(statusTypes)
                    .List()
                    .Select(x => x.ID)
                    .ToList();

                search.RootCriteria.CreateAlias("Statuses", "st")
                          .Add(Restrictions.In("st.ID", statuses));
            }

            if (clusterQuery.Profiles != null && clusterQuery.Profiles.Any())
            {
                search.RootCriteria.CreateAlias("Profiles", "prs")
                         .Add(Restrictions.In("prs.ID", clusterQuery.Profiles));
            }

            var profilesAreAtRemoteArea = clusterQuery.ProfilesAreAtRemoteArea ?? false;
            var profilesAreEntrepreneur = clusterQuery.ProfilesAreEntrepreneur ?? false;
            if (profilesAreAtRemoteArea || profilesAreEntrepreneur)
            {
                var searchProfiles = this.Session.QueryOver<Models.Domain.Profile>();

                if (profilesAreAtRemoteArea)
                {
                    searchProfiles = searchProfiles.Where(x => x.IsRemoteArea);
                }

                if (profilesAreEntrepreneur)
                {
                    searchProfiles = searchProfiles.Where(x => x.IsEntrepreneur);
                }

                var profileList = searchProfiles.List().Select(x => x.ID);

                if (profileList.Any())
                {
                    search.RootCriteria.CreateAlias("Profiles", "atprof")
                          .Add(Restrictions.In("atprof.ID", profileList.ToArray()));
                }
                else
                {
                    //No profiles match the search, force return 0 results
                    search = search.Where(x => x.ID == Guid.Empty);
                }
            }

            result.TotalRowCount = search.RowCount();
            IList<Cluster> list = ApplySkipAndTake(settings, search);
            bool asc = settings.SortAscending == null || (bool)settings.SortAscending;
            
            if (list.Any())
            {
                list = asc ? list.OrderBy(x => x.Name).ToList() : list.OrderByDescending(x => x.Name).ToList();
                foreach (Cluster cluster in list)
                {
                    result.List.Add(Services.Dto.Cluster.Convert(cluster));
                }
            }

            return result;
        }

        #endregion

        #region Methods

        /// <summary>
        /// The delete cluster.
        /// </summary>
        /// <param name="cmd">
        /// The clusterQuery.
        /// </param>
        internal void DeleteCluster(DeleteClusterCommand cmd)
        {
            if (cmd.Dto == Guid.Empty)
            {
                return;
            }

            var cluster = this.Session.Get<Cluster>(cmd.Dto);

            if (cluster == null)
            {
                return;
            }

            foreach (var status in cluster.Statuses)
            {
                this.Session.Delete(status);
            }

            foreach (var method in cluster.Methods)
            {
                this.Session.Delete(method);
            }

            this.Log.CreateLogEntry(
                        cluster.ID,
                        string.Format("Klyngen '{0}' blev slettet.", cluster.Name),
                        Enums.LogEntryType.Cluster,
                            Enums.LogEntryActionType.Delete
                        );
            this.Session.Delete(cluster);
        }

        /// <summary>
        /// The update cluster.
        /// </summary>
        /// <param name="cmd">
        /// The clusterQuery.
        /// </param>
        internal void UpdateCluster(UpdateClusterCommand cmd)
        {
            if (cmd.Dto != null)
            {
                var cluster = this.Session.Get<Cluster>(cmd.Dto.ID);

                if (cluster != null)
                {
                    cluster.Name = cmd.Dto.Name;
                    cluster.Location = this.Session.Get<Location>(cmd.Dto.Location);
                    cluster.Date = cmd.Dto.Date;

                    if (cmd.Dto.OwnerProjects != null && cmd.Dto.OwnerProjects.Any())
                    {
                        cluster.OwnerProjects.Clear();
                        foreach (var project in cmd.Dto.OwnerProjects.Select(prID => this.Session.Get<Project>(prID)).Where(project => project != null))
                        {
                            cluster.OwnerProjects.Add(project);
                        }
                    }

                    cluster.Profiles.Clear();
                    if (cmd.Dto.Profiles != null && cmd.Dto.Profiles.Any())
                    {
                        foreach (Guid profileID in cmd.Dto.Profiles)
                        {
                            var profile = this.Session.Get<Profile>(profileID);
                            if (profile != null)
                            {
                                cluster.Profiles.Add(profile);
                            }
                        }
                    }

                    if (cmd.Dto.Methods != null && cmd.Dto.Methods.Any())
                    {
                        foreach (Guid methodID in cmd.Dto.Methods)
                        {
                            // If it does not already exist in the search
                            if (cluster.Methods.All(x => x.ID != methodID))
                            {
                                var method = this.Session.Get<ClusterMethod>(methodID);
                                if (method != null)
                                {
                                    cluster.Methods.Add(method);
                                }
                            }
                        }
                    }

                    if (cmd.Dto.Statuses != null && cmd.Dto.Statuses.Any())
                    {
                        foreach (Guid statusID in cmd.Dto.Statuses)
                        {
                            // If it does not already exist in the search
                            if (cluster.Statuses.All(cs => cs.ID != statusID))
                            {
                                var status = this.Session.Get<ClusterStatus>(statusID);
                                if (status != null)
                                {
                                    cluster.Statuses.Add(status);
                                }
                            }

                        }
                    }

                    this.Session.Update(cluster);
                    this.Log.CreateLogEntry(
                        cluster.ID,
                        string.Format("Klyngen '{0}' blev opdateret.", cluster.Name),
                        Enums.LogEntryType.Cluster,
                            Enums.LogEntryActionType.Update
                        );
                }
            }
        }

        #endregion
    }
}